package org.calzz.jajb;

import java.util.HashSet;
import java.util.Set;

import org.calzz.jajb.converter.BeanConverter;
import org.calzz.jajb.converter.IConverter;
import org.calzz.jajb.io.IWriter;

/**
 * Serializer of Java beans into JSON objects, which are later written to a
 * chosen implementation of <code>IWriter</code> interface.
 * 
 * @author Marek Brodziak <marek.brodziak@gmail.com>
 * @author Piotr Pejas <piotr.pejas@gmail.com>
 * @since Feb 6, 2008
 */
public class JsonSerializer {

	public static final String NULL = "null";
	private Set<IConverter> converters = new HashSet<IConverter>();
	private IWriter writer;
	private boolean serializeNullValues = false;

	/**
	 * Default contructor for this serializer.
	 * @param writer <code>IWriter</code> interface implementation
	 */
	public JsonSerializer(IWriter writer) {
		this.writer = writer;
	}

	/**
	 * Processes a given object using registered converters and writes it to the
	 * given reader. Should be called directly only from converter
	 * <code>marshal</code> methods.
	 * @param obj object to be serialized
	 * @param name name of the object
	 * @param writer <code>IWriter</code> interface implementation of choice
	 */
	public void process(Object obj, String name, IWriter writer) {
		if (obj == null) {
			if (serializeNullValues) {
				writer.writeKey(name);
				writer.writeValue(NULL);
			}
			return;
		}

		for (IConverter converter : converters) {
			if (converter.canConvert(obj.getClass())) {
				converter.marshal(obj, name, writer, this);
				return;
			}
		}

		new BeanConverter().marshal(obj, name, writer, this);
	}

	/**
	 * Main method to be called when serializing an object.
	 * @param obj object to be serialized
	 * @return JSON object as a <code>String</code>
	 */
	public String serialize(Object obj) {
		process(obj, null, writer);
		return writer.toString();
	}

	/**
	 * Sets a collection of converters used by this serializer. Method to be
	 * used with Spring framework.
	 * @param converters set of converters for the serializer
	 */
	public void setConverters(Set<IConverter> converters) {
		this.converters = converters;
	}

	/**
	 * Registers a single converter in the serializer environment.
	 * @param converter converter to be registered
	 */
	public void registerConverter(IConverter converter) {
		this.converters.add(converter);
	}

	/**
	 * @return true, if "null" values are being serialized, false if they're
	 *         begin skipped
	 */
	public boolean isSerializeNullValues() {
		return serializeNullValues;
	}

	/**
	 * Sets a flag which determines whether to include "null" values in the
	 * result JSON object.
	 * @param writeNullValues
	 */
	public void setSerializeNullValues(boolean writeNullValues) {
		this.serializeNullValues = writeNullValues;
	}

}
